Skip to content

Conversation

@flying-sheep
Copy link
Member

@flying-sheep flying-sheep commented Jan 8, 2026

User API design

  1. create AdPath instances by descending into a central accessor constant A:
    (A[:, :], lambda ad: ad.X),
    (A[:, "gene-3"], lambda ad: ad[:, "gene-3"].X.flatten()),
    (A["cell-5", :], lambda ad: ad["cell-5"].X.flatten()),
    (A.obs["type"], lambda ad: ad.obs["type"]),
    (A.obs.index, lambda ad: ad.obs.index.values),
    (A.layers["a"][:, :], lambda ad: ad.layers["a"].copy().toarray()),
    (
    A.layers["a"][:, "gene-18"],
    lambda ad: ad[:, "gene-18"].layers["a"].copy().toarray().flatten(),
    ),
    (
    A.layers["a"]["cell-77", :],
    lambda ad: ad["cell-77"].layers["a"].copy().toarray().flatten(),
    ),
    (A.obsm["umap"][0], lambda ad: ad.obsm["umap"][:, 0]),
    (A.obsm["umap"][1], lambda ad: ad.obsm["umap"][:, 1]),
    (A.varp["cons"]["gene-46", :], lambda ad: ad.varp["cons"][46, :].toarray()),
    (A.varp["cons"][:, "gene-46"], lambda ad: ad.varp["cons"][:, 46].toarray()),
  2. Inspect AdPath instance, e.g. to figure out which axes the resulting vector spans:
    pytest.param(A.obsm["c"][:, 0], {"obs"}, id="obsm"),
    pytest.param(A.varp["d"][:, :], ("var", "var"), id="varp"),
    pytest.param(A.varp["d"][:, "c2"], {"var"}, id="varp-col"),
    ],
    )
    def test_axes(ad_path: AdPath, axes: Collection[Literal["obs", "var"]]) -> None:
    assert ad_path.axes == axes
  3. Call AdPath instance to extract a vector (see 1. for examples)

subclassing

… is a direct goal of this, as people should be able to use AdPath subclasses. This means that

  1. the AdPath API is minimal and inspection can be done through .acc (could be even more minimal by just putting it all into a container?):
    acc: VecAcc[Self, I]
    idx: I
    @cached_property
    def axes(self) -> Axes:
  2. It’s trivial to create a new AdAcc constant that produces your own AdPath subclass:
    A = AdAcc(path_class=AdPath)
  3. The data flow is easy to understand:
    1. User uses VecAcc.__getitem__ to get an AdPath or a list of them. In that process,
      1. __getitem__ calls process_idx which verifies and simplifies the index
      2. axes, __repr__, and idx_repr get called too to validate things work
    2. The public API of AdPath can be used, which basically just delegates to VecAcc’s axes, __repr__, idx_repr, and __call__

So in the end everything except for __call__ is validated, i.e. VecAcc.__getitem__ raises exceptions on misuse

class VecAcc[P: AdPath[I], I](abc.ABC): # type: ignore
path_class: type[P]
def process_idx(self, idx: Any, /) -> I:
self.axes(idx)
return idx
def __getitem__(self, idx: Any, /) -> P:
idx = self.process_idx(idx)
return self.path_class(self, idx) # type: ignore
@abc.abstractmethod
def axes(self, idx: I, /) -> Axes: ...
@abc.abstractmethod
def __repr__(self, /) -> str: ...
@abc.abstractmethod
def idx_repr(self, idx: I, /) -> str: ...
@abc.abstractmethod
def __call__(self, adata: AnnData, idx: I, /) -> Vector: ...

TODO:

  • serialization
  • docs (especially data flow)
    • nomenclature: “vector” vs “array”, “accessor”, “vector accessor”, “reference”, “path”, … get it straight!
      Especially since we already call these accessors! Instance namespaces (adata.custom) #1869
  • Array types: fix typing and test with all array types we support
  • improve __contains__ so it doesn’t try to access the array
  • add JSON schema
    • also add it to docs (linked from from_json/to_json)
    • and test it
  • terminology: axis → dim
  • (maybe) runtime autocompletion (__dir__)

@codecov
Copy link

codecov bot commented Jan 8, 2026

❌ 6 Tests Failed:

Tests completed Failed Passed Skipped
5281 6 5275 2572
View the top 3 failed test(s) by shortest run time
tests.test_base::test_1d_slice_dtypes
Stack Traces | 0.006s run time
#x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mtest_1d_slice_dtypes#x1B[39;49;00m() -> #x1B[94mNone#x1B[39;49;00m:#x1B[90m#x1B[39;49;00m
        N, M = #x1B[94m10#x1B[39;49;00m, #x1B[94m20#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        obs_df = pd.DataFrame(#x1B[90m#x1B[39;49;00m
            #x1B[96mdict#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
                cat=pd.Categorical(np.arange(N, dtype=#x1B[96mint#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
                #x1B[96mint#x1B[39;49;00m=np.arange(N, dtype=#x1B[96mint#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
                #x1B[96mfloat#x1B[39;49;00m=np.arange(N, dtype=#x1B[96mfloat#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
                obj=[#x1B[96mstr#x1B[39;49;00m(i) #x1B[94mfor#x1B[39;49;00m i #x1B[95min#x1B[39;49;00m np.arange(N, dtype=#x1B[96mint#x1B[39;49;00m)],#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
            index=[#x1B[33mf#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[33mcell#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mi#x1B[33m}#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m #x1B[94mfor#x1B[39;49;00m i #x1B[95min#x1B[39;49;00m np.arange(N, dtype=#x1B[96mint#x1B[39;49;00m)],#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        var_df = pd.DataFrame(#x1B[90m#x1B[39;49;00m
            #x1B[96mdict#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
                cat=pd.Categorical(np.arange(M, dtype=#x1B[96mint#x1B[39;49;00m)),#x1B[90m#x1B[39;49;00m
                #x1B[96mint#x1B[39;49;00m=np.arange(M, dtype=#x1B[96mint#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
                #x1B[96mfloat#x1B[39;49;00m=np.arange(M, dtype=#x1B[96mfloat#x1B[39;49;00m),#x1B[90m#x1B[39;49;00m
                obj=[#x1B[96mstr#x1B[39;49;00m(i) #x1B[94mfor#x1B[39;49;00m i #x1B[95min#x1B[39;49;00m np.arange(M, dtype=#x1B[96mint#x1B[39;49;00m)],#x1B[90m#x1B[39;49;00m
            ),#x1B[90m#x1B[39;49;00m
            index=[#x1B[33mf#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[33mgene#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mi#x1B[33m}#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m #x1B[94mfor#x1B[39;49;00m i #x1B[95min#x1B[39;49;00m np.arange(M, dtype=#x1B[96mint#x1B[39;49;00m)],#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        adata = AnnData(X=np.random.random((N, M)), obs=obs_df, var=var_df)#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        new_obs_df = pd.DataFrame(index=adata.obs_names)#x1B[90m#x1B[39;49;00m
        #x1B[94mfor#x1B[39;49;00m k #x1B[95min#x1B[39;49;00m obs_df.columns:#x1B[90m#x1B[39;49;00m
>           new_obs_df[k] = A.obs[k](adata)#x1B[90m#x1B[39;49;00m
                            ^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           TypeError: 'AdRef' object is not callable#x1B[0m

#x1B[1m#x1B[31mtests/test_base.py#x1B[0m:707: TypeError
tests.accessors.test_get::test_get_values[A.obsm['umap'][:, 0]-sp.csr_array]
Stack Traces | 0.007s run time
adata = AnnData object with n_obs × n_vars = 100 × 50
    obs: 'type'
    var: 'grp'
    obsm: 'umap'
    layers: 'a'
    varp: 'cons'
request = <SubRequest 'get_test_params' for <Function test_get_values[A.obsm['umap'][:, 0]-sp.csr_array]>>

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.fixture(#x1B[90m#x1B[39;49;00m
        params=[#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                (ad_ref, ad_expected, *typ.values, convert),#x1B[90m#x1B[39;49;00m
                marks=typ.marks,#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33mf#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mad_ref#x1B[33m}#x1B[39;49;00m#x1B[33m-#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mtyp.id#x1B[33m}#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m paths, types, convert #x1B[95min#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
                (ND_PATHS, ND_TYPES, convert_ndarrays),#x1B[90m#x1B[39;49;00m
                (DF_PATHS, DF_TYPES, convert_dataframes),#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m ad_ref, ad_expected #x1B[95min#x1B[39;49;00m paths#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m typ #x1B[95min#x1B[39;49;00m types#x1B[90m#x1B[39;49;00m
        ]#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mget_test_params#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        adata: AnnData, request: pytest.FixtureRequest#x1B[90m#x1B[39;49;00m
    ) -> #x1B[96mtuple#x1B[39;49;00m[AnnData, AdRef, #x1B[96mtype#x1B[39;49;00m[InMemoryArray], InMemoryArray]:#x1B[90m#x1B[39;49;00m
        ad_ref, ad_expected, convert_array, type_expected_1d, type_expected_2d, convert = (#x1B[90m#x1B[39;49;00m
            request.param#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        convert(adata, convert_array)#x1B[90m#x1B[39;49;00m
        #x1B[94mreturn#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
            adata,#x1B[90m#x1B[39;49;00m
            ad_ref,#x1B[90m#x1B[39;49;00m
            type_expected_1d #x1B[94mif#x1B[39;49;00m #x1B[96mlen#x1B[39;49;00m(ad_ref.dims) == #x1B[94m1#x1B[39;49;00m #x1B[94melse#x1B[39;49;00m type_expected_2d,#x1B[90m#x1B[39;49;00m
>           _expected2np(ad_expected(adata), ad_ref),#x1B[90m#x1B[39;49;00m
                         ^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m

#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:187: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:48: in <lambda>
    #x1B[0m(A.obsm[#x1B[33m"#x1B[39;49;00m#x1B[33mumap#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][#x1B[94m0#x1B[39;49;00m], #x1B[94mlambda#x1B[39;49;00m ad: ad.obsm[#x1B[33m"#x1B[39;49;00m#x1B[33mumap#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][:, #x1B[94m0#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
                                   ^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:69: in __getitem__
    #x1B[0m#x1B[96mself#x1B[39;49;00m._raise_on_1d_array_slice()#x1B[90m#x1B[39;49;00m
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <100x2 sparse array of type '<class 'numpy.float64'>'
	with 200 stored elements in Compressed Sparse Row format>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m_raise_on_1d_array_slice#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""We do not currently support 1D sparse arrays.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    This function is called each time that a 1D array would#x1B[39;49;00m
    #x1B[33m    result, raising an error instead.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    Once 1D sparse arrays are implemented, it should be removed.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mfrom#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[04m#x1B[96mscipy#x1B[39;49;00m#x1B[04m#x1B[96m.#x1B[39;49;00m#x1B[04m#x1B[96msparse#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[94mimport#x1B[39;49;00m sparray#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, sparray):#x1B[90m#x1B[39;49;00m
>           #x1B[94mraise#x1B[39;49;00m #x1B[96mNotImplementedError#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mWe have not yet implemented 1D sparse slices; #x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mplease index using explicit indices, e.g. `x[:, [0]]`#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           NotImplementedError: We have not yet implemented 1D sparse slices; please index using explicit indices, e.g. `x[:, [0]]`#x1B[0m

#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:46: NotImplementedError
tests.accessors.test_get::test_get_values[A.obsm['umap'][:, 1]-sp.csr_array]
Stack Traces | 0.007s run time
adata = AnnData object with n_obs × n_vars = 100 × 50
    obs: 'type'
    var: 'grp'
    obsm: 'umap'
    layers: 'a'
    varp: 'cons'
request = <SubRequest 'get_test_params' for <Function test_get_values[A.obsm['umap'][:, 1]-sp.csr_array]>>

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.fixture(#x1B[90m#x1B[39;49;00m
        params=[#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                (ad_ref, ad_expected, *typ.values, convert),#x1B[90m#x1B[39;49;00m
                marks=typ.marks,#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33mf#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mad_ref#x1B[33m}#x1B[39;49;00m#x1B[33m-#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mtyp.id#x1B[33m}#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m paths, types, convert #x1B[95min#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
                (ND_PATHS, ND_TYPES, convert_ndarrays),#x1B[90m#x1B[39;49;00m
                (DF_PATHS, DF_TYPES, convert_dataframes),#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m ad_ref, ad_expected #x1B[95min#x1B[39;49;00m paths#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m typ #x1B[95min#x1B[39;49;00m types#x1B[90m#x1B[39;49;00m
        ]#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mget_test_params#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        adata: AnnData, request: pytest.FixtureRequest#x1B[90m#x1B[39;49;00m
    ) -> #x1B[96mtuple#x1B[39;49;00m[AnnData, AdRef, #x1B[96mtype#x1B[39;49;00m[InMemoryArray], InMemoryArray]:#x1B[90m#x1B[39;49;00m
        ad_ref, ad_expected, convert_array, type_expected_1d, type_expected_2d, convert = (#x1B[90m#x1B[39;49;00m
            request.param#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        convert(adata, convert_array)#x1B[90m#x1B[39;49;00m
        #x1B[94mreturn#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
            adata,#x1B[90m#x1B[39;49;00m
            ad_ref,#x1B[90m#x1B[39;49;00m
            type_expected_1d #x1B[94mif#x1B[39;49;00m #x1B[96mlen#x1B[39;49;00m(ad_ref.dims) == #x1B[94m1#x1B[39;49;00m #x1B[94melse#x1B[39;49;00m type_expected_2d,#x1B[90m#x1B[39;49;00m
>           _expected2np(ad_expected(adata), ad_ref),#x1B[90m#x1B[39;49;00m
                         ^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m

#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:187: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:49: in <lambda>
    #x1B[0m(A.obsm[#x1B[33m"#x1B[39;49;00m#x1B[33mumap#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][#x1B[94m1#x1B[39;49;00m], #x1B[94mlambda#x1B[39;49;00m ad: ad.obsm[#x1B[33m"#x1B[39;49;00m#x1B[33mumap#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][:, #x1B[94m1#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
                                   ^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:69: in __getitem__
    #x1B[0m#x1B[96mself#x1B[39;49;00m._raise_on_1d_array_slice()#x1B[90m#x1B[39;49;00m
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <100x2 sparse array of type '<class 'numpy.float64'>'
	with 200 stored elements in Compressed Sparse Row format>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m_raise_on_1d_array_slice#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""We do not currently support 1D sparse arrays.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    This function is called each time that a 1D array would#x1B[39;49;00m
    #x1B[33m    result, raising an error instead.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    Once 1D sparse arrays are implemented, it should be removed.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mfrom#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[04m#x1B[96mscipy#x1B[39;49;00m#x1B[04m#x1B[96m.#x1B[39;49;00m#x1B[04m#x1B[96msparse#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[94mimport#x1B[39;49;00m sparray#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, sparray):#x1B[90m#x1B[39;49;00m
>           #x1B[94mraise#x1B[39;49;00m #x1B[96mNotImplementedError#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mWe have not yet implemented 1D sparse slices; #x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mplease index using explicit indices, e.g. `x[:, [0]]`#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           NotImplementedError: We have not yet implemented 1D sparse slices; please index using explicit indices, e.g. `x[:, [0]]`#x1B[0m

#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:46: NotImplementedError
tests.accessors.test_get::test_get_values[A.varp['cons']['gene-46', :]-sp.csr_array]
Stack Traces | 0.009s run time
adata = AnnData object with n_obs × n_vars = 100 × 50
    obs: 'type'
    var: 'grp'
    obsm: 'umap'
    layers: 'a'
    varp: 'cons'
request = <SubRequest 'get_test_params' for <Function test_get_values[A.varp['cons']['gene-46', :]-sp.csr_array]>>

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.fixture(#x1B[90m#x1B[39;49;00m
        params=[#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                (ad_ref, ad_expected, *typ.values, convert),#x1B[90m#x1B[39;49;00m
                marks=typ.marks,#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33mf#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mad_ref#x1B[33m}#x1B[39;49;00m#x1B[33m-#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mtyp.id#x1B[33m}#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m paths, types, convert #x1B[95min#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
                (ND_PATHS, ND_TYPES, convert_ndarrays),#x1B[90m#x1B[39;49;00m
                (DF_PATHS, DF_TYPES, convert_dataframes),#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m ad_ref, ad_expected #x1B[95min#x1B[39;49;00m paths#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m typ #x1B[95min#x1B[39;49;00m types#x1B[90m#x1B[39;49;00m
        ]#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mget_test_params#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        adata: AnnData, request: pytest.FixtureRequest#x1B[90m#x1B[39;49;00m
    ) -> #x1B[96mtuple#x1B[39;49;00m[AnnData, AdRef, #x1B[96mtype#x1B[39;49;00m[InMemoryArray], InMemoryArray]:#x1B[90m#x1B[39;49;00m
        ad_ref, ad_expected, convert_array, type_expected_1d, type_expected_2d, convert = (#x1B[90m#x1B[39;49;00m
            request.param#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        convert(adata, convert_array)#x1B[90m#x1B[39;49;00m
        #x1B[94mreturn#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
            adata,#x1B[90m#x1B[39;49;00m
            ad_ref,#x1B[90m#x1B[39;49;00m
            type_expected_1d #x1B[94mif#x1B[39;49;00m #x1B[96mlen#x1B[39;49;00m(ad_ref.dims) == #x1B[94m1#x1B[39;49;00m #x1B[94melse#x1B[39;49;00m type_expected_2d,#x1B[90m#x1B[39;49;00m
>           _expected2np(ad_expected(adata), ad_ref),#x1B[90m#x1B[39;49;00m
                         ^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m

#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:187: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:50: in <lambda>
    #x1B[0m(A.varp[#x1B[33m"#x1B[39;49;00m#x1B[33mcons#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][#x1B[33m"#x1B[39;49;00m#x1B[33mgene-46#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m, :], #x1B[94mlambda#x1B[39;49;00m ad: ad.varp[#x1B[33m"#x1B[39;49;00m#x1B[33mcons#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][#x1B[94m46#x1B[39;49;00m, :]),#x1B[90m#x1B[39;49;00m
                                              ^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:59: in __getitem__
    #x1B[0m#x1B[96mself#x1B[39;49;00m._raise_on_1d_array_slice()#x1B[90m#x1B[39;49;00m
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <50x50 sparse array of type '<class 'numpy.float64'>'
	with 25 stored elements in Compressed Sparse Row format>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m_raise_on_1d_array_slice#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""We do not currently support 1D sparse arrays.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    This function is called each time that a 1D array would#x1B[39;49;00m
    #x1B[33m    result, raising an error instead.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    Once 1D sparse arrays are implemented, it should be removed.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mfrom#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[04m#x1B[96mscipy#x1B[39;49;00m#x1B[04m#x1B[96m.#x1B[39;49;00m#x1B[04m#x1B[96msparse#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[94mimport#x1B[39;49;00m sparray#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, sparray):#x1B[90m#x1B[39;49;00m
>           #x1B[94mraise#x1B[39;49;00m #x1B[96mNotImplementedError#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mWe have not yet implemented 1D sparse slices; #x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mplease index using explicit indices, e.g. `x[:, [0]]`#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           NotImplementedError: We have not yet implemented 1D sparse slices; please index using explicit indices, e.g. `x[:, [0]]`#x1B[0m

#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:46: NotImplementedError
tests.accessors.test_get::test_get_values[A.varp['cons'][:, 'gene-46']-sp.csr_array]
Stack Traces | 0.009s run time
adata = AnnData object with n_obs × n_vars = 100 × 50
    obs: 'type'
    var: 'grp'
    obsm: 'umap'
    layers: 'a'
    varp: 'cons'
request = <SubRequest 'get_test_params' for <Function test_get_values[A.varp['cons'][:, 'gene-46']-sp.csr_array]>>

    #x1B[0m#x1B[37m@pytest#x1B[39;49;00m.fixture(#x1B[90m#x1B[39;49;00m
        params=[#x1B[90m#x1B[39;49;00m
            pytest.param(#x1B[90m#x1B[39;49;00m
                (ad_ref, ad_expected, *typ.values, convert),#x1B[90m#x1B[39;49;00m
                marks=typ.marks,#x1B[90m#x1B[39;49;00m
                #x1B[96mid#x1B[39;49;00m=#x1B[33mf#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mad_ref#x1B[33m}#x1B[39;49;00m#x1B[33m-#x1B[39;49;00m#x1B[33m{#x1B[39;49;00mtyp.id#x1B[33m}#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m,#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m paths, types, convert #x1B[95min#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
                (ND_PATHS, ND_TYPES, convert_ndarrays),#x1B[90m#x1B[39;49;00m
                (DF_PATHS, DF_TYPES, convert_dataframes),#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m ad_ref, ad_expected #x1B[95min#x1B[39;49;00m paths#x1B[90m#x1B[39;49;00m
            #x1B[94mfor#x1B[39;49;00m typ #x1B[95min#x1B[39;49;00m types#x1B[90m#x1B[39;49;00m
        ]#x1B[90m#x1B[39;49;00m
    )#x1B[90m#x1B[39;49;00m
    #x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92mget_test_params#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
        adata: AnnData, request: pytest.FixtureRequest#x1B[90m#x1B[39;49;00m
    ) -> #x1B[96mtuple#x1B[39;49;00m[AnnData, AdRef, #x1B[96mtype#x1B[39;49;00m[InMemoryArray], InMemoryArray]:#x1B[90m#x1B[39;49;00m
        ad_ref, ad_expected, convert_array, type_expected_1d, type_expected_2d, convert = (#x1B[90m#x1B[39;49;00m
            request.param#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m
        convert(adata, convert_array)#x1B[90m#x1B[39;49;00m
        #x1B[94mreturn#x1B[39;49;00m (#x1B[90m#x1B[39;49;00m
            adata,#x1B[90m#x1B[39;49;00m
            ad_ref,#x1B[90m#x1B[39;49;00m
            type_expected_1d #x1B[94mif#x1B[39;49;00m #x1B[96mlen#x1B[39;49;00m(ad_ref.dims) == #x1B[94m1#x1B[39;49;00m #x1B[94melse#x1B[39;49;00m type_expected_2d,#x1B[90m#x1B[39;49;00m
>           _expected2np(ad_expected(adata), ad_ref),#x1B[90m#x1B[39;49;00m
                         ^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
        )#x1B[90m#x1B[39;49;00m

#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:187: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
#x1B[1m#x1B[31mtests/accessors/test_get.py#x1B[0m:51: in <lambda>
    #x1B[0m(A.varp[#x1B[33m"#x1B[39;49;00m#x1B[33mcons#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][:, #x1B[33m"#x1B[39;49;00m#x1B[33mgene-46#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m], #x1B[94mlambda#x1B[39;49;00m ad: ad.varp[#x1B[33m"#x1B[39;49;00m#x1B[33mcons#x1B[39;49;00m#x1B[33m"#x1B[39;49;00m][:, #x1B[94m46#x1B[39;49;00m]),#x1B[90m#x1B[39;49;00m
                                              ^^^^^^^^^^^^^^^^^^^^^^#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:69: in __getitem__
    #x1B[0m#x1B[96mself#x1B[39;49;00m._raise_on_1d_array_slice()#x1B[90m#x1B[39;49;00m
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <50x50 sparse array of type '<class 'numpy.float64'>'
	with 25 stored elements in Compressed Sparse Row format>

    #x1B[0m#x1B[94mdef#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[92m_raise_on_1d_array_slice#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m):#x1B[90m#x1B[39;49;00m
    #x1B[90m    #x1B[39;49;00m#x1B[33m"""We do not currently support 1D sparse arrays.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    This function is called each time that a 1D array would#x1B[39;49;00m
    #x1B[33m    result, raising an error instead.#x1B[39;49;00m
    #x1B[33m#x1B[39;49;00m
    #x1B[33m    Once 1D sparse arrays are implemented, it should be removed.#x1B[39;49;00m
    #x1B[33m    """#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
        #x1B[94mfrom#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[04m#x1B[96mscipy#x1B[39;49;00m#x1B[04m#x1B[96m.#x1B[39;49;00m#x1B[04m#x1B[96msparse#x1B[39;49;00m#x1B[90m #x1B[39;49;00m#x1B[94mimport#x1B[39;49;00m sparray#x1B[90m#x1B[39;49;00m
    #x1B[90m#x1B[39;49;00m
        #x1B[94mif#x1B[39;49;00m #x1B[96misinstance#x1B[39;49;00m(#x1B[96mself#x1B[39;49;00m, sparray):#x1B[90m#x1B[39;49;00m
>           #x1B[94mraise#x1B[39;49;00m #x1B[96mNotImplementedError#x1B[39;49;00m(#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mWe have not yet implemented 1D sparse slices; #x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
                #x1B[33m'#x1B[39;49;00m#x1B[33mplease index using explicit indices, e.g. `x[:, [0]]`#x1B[39;49;00m#x1B[33m'#x1B[39;49;00m#x1B[90m#x1B[39;49;00m
            )#x1B[90m#x1B[39;49;00m
#x1B[1m#x1B[31mE           NotImplementedError: We have not yet implemented 1D sparse slices; please index using explicit indices, e.g. `x[:, [0]]`#x1B[0m

#x1B[1m#x1B[31m../../../..../anndata/88DSKUwu/hatch-test.min/lib/python3.12.../scipy/sparse/_index.py#x1B[0m:46: NotImplementedError
docs.accessors.rst::accessors.rst
Stack Traces | 0.202s run time
028 
029     E.g. to check if `adata.varm["PCs"]` has at least 30 columns:
030 
031     >>> A.varm["PCs"][:, 30] in adata
032     True
033 
034     or to extract the referenced vector:
035 
036     >>> ref = A.obs["louvain"]
037     >>> adata[ref].categories[:2]
Expected:
    Index(['CD4 T cells', 'CD14+ Monocytes'], dtype='str')
Got:
    Index(['CD4 T cells', 'CD14+ Monocytes'], dtype='object')

#x1B[1m#x1B[.../anndata/docs/accessors.rst#x1B[0m:37: DocTestFailure

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@flying-sheep flying-sheep added this to the 0.13.0 milestone Jan 15, 2026
@flying-sheep
Copy link
Member Author

@ilan-gold I didn’t update the docs since your last review, but the rest should be finished now!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reference paths / accessors

2 participants